/**
 * GeneralScript.java
 * ************************** 
 * @date May 28, 2011
 * @author Michele Tamburini
 * 
 * **************************
 */
package infrastructureAPI.backendGate;

import gossipServices.basic.nodeDescriptors.ConcreteNodeDescriptor;
import infrastructureAPI.ConcreteRequester;
import infrastructureAPI.Requester;
import infrastructureAPI.APIMessages.ResponseMessage;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.rmi.RemoteException;
import java.util.List;
import java.util.Vector;

import messages.Message;

/**
 * Description:
 * The GeneralScript is an abstract class that can be used
 * as a base class for the implementation of an API script.
 * The inheriting class need simply to use the default and
 * unique constructor passing the String[] args parameter,
 * and implementing the abstract method specific for 
 * the subclass.
 * The constructor of the GeneralScript follows the sequence
 * of steps:
 *  1. 	Translate the String[] args argument into the 
 *  	"Vector<String> arguments" variable that's easier to handle,
 *  2.	checks the optional parameters and calls the
 *  	"checkNotGeneralOptionalParameters()" function,
 *  3.	verifies the mandatory parameters and calling the
 *  	"verifyMandatoryParameters()" method,
 *  4.	composes the mandatory parameters calling the method:
 *  	"composeMandatoryParameters()",
 *  5.	retrieves the local address and the local node name,
 *  6.	composes the local Requester node,
 *  7.	instantiates the APIScript: "instantiateAPIScript()",
 *  8.	initializes the ResponseReceiverScriptAPI,
 *  9.	marshals the RequestMessage according to the procedure
 *  	specified by the APIScript,
 *  10.	delivers the composed RequestMessage to the Back-End of the
 *  	local node,
 *  11.	waits for incoming requests and calls the method:
 *  	"waitingIncomingResposes()",
 *  12.	disconnects the ResponseReceiverScriptAPI.
 *
 */
public abstract class GeneralScript {
	
	/**
	 * The name of the node from which the script 
	 * is launched.
	 */
	protected static String nodeName;
	
	/**
	 * The address of the local node used to launch the 
	 * script.
	 */
	protected static InetAddress nodeAddress;
	
	/**
	 * The string used to register the current script
	 * into the rmiregistry. The registration to the 
	 * rmiregistry is required in order to receive incoming
	 * requests.
	 */
	protected static String registeredServiceName;
	
	/**
	 * The 'String[] args' parameter in input to the 
	 * main is translated into the 'arguments' variable
	 * tha's presumably simpler to handle.
	 */
	protected static List<String> arguments;
	
	/**
	 * Variable containing the index of the last optional
	 * parameter occurrence. 
	 * You can use this as a starting point to
	 * analyze the mandatory parameters.
	 */
	protected static int optionalParameterIndex=0;
	
	/**
	 * The script that finally will be 
	 */
	protected static APIScript apiFunctionScript;
	
	/**
	 * The Requester node from which the script has been launched.
	 */
	protected static Requester requester;
	
	
	public GeneralScript(String[] args) {
		executeSharedSteps(args);
	}
	
	// @for debug only!!!
//	protected GeneralScript(){}
	
	/**
	 * 
	 */
	protected void executeSharedSteps(String[] args) {
		boolean ret;
		
		// Translation of the input arguments
		// -----------------------
		arguments = APIRoutineFunctions.convertToVector(args);
		
		// Check of optional parameters
		// -----------------------
		checkInputNodeName();				
		checkNotGeneralOptionalParameters();
		
		// Mandatory parameters verification
		// -----------------------
		if (verifyMandatoryParameters() == false){
			return;
		}
		
		// Mandatory parameters composition
		// -----------------------
		composeMandatoryParameters();
	
		try {
			
			// Retrieve local address
			// -----------------------
			nodeAddress = InetAddress.getLocalHost();
			
			// Retrieve registered service name
			// -----------------------			
			registeredServiceName = produceRegisteredName();
			
			// Requester composition
			// -----------------------
			requester = new ConcreteRequester(nodeAddress, registeredServiceName, 
					APIRoutineFunctions.RMIREGISTRY_STADARD_PORT);
			
			// Instantiation of the APIScript: MonitorInstances
			// -----------------------
			apiFunctionScript = instantiateAPIScript();
			
			// ResponseReceiver initialization
			// -----------------------
			APIRoutineFunctions.initializeResponseReceiver(nodeAddress, registeredServiceName);
			
			// RequestMessage: Marshaling
			// -----------------------
			Message request =  APIRoutineFunctions.marshal(nodeAddress, nodeName, apiFunctionScript);
			
			// RequestMessage: Delivering
			// -----------------------
			System.out.println("delivering request message: "+request+"\nfrom node: "+nodeName);
			ret = APIRoutineFunctions.deliverMessage(request, nodeName);
			if(ret){
				System.out.println("SUCCESSFUL!\n Request: "+
						this.getClass().getSimpleName()+" delivered with message:\n"
						+request);
			}else{
				System.out.println("FAILED delivering message:\n"+request+
						"\nto node: "+nodeName+" on "+nodeAddress);
			}
			
			// Waiting for incoming replies
			// -----------------------
			waitingIncomingResposes();
			
			// ResponseReceiver disconnection
			// -----------------------
			APIRoutineFunctions.disconnectReceiver();
			
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}


	public String produceRegisteredName(){
		return this.getClass().getSimpleName();
	}
	
	public abstract void checkNotGeneralOptionalParameters();
	
	public abstract boolean verifyMandatoryParameters();
	
	public abstract void composeMandatoryParameters();
	
	public abstract APIScript instantiateAPIScript();
	
	public abstract void waitingIncomingResposes();

	/**
	 * Extracts the name of the node from which the request
	 * is being submitted.
	 *  
	 */
	protected void checkInputNodeName() {
		if(arguments.contains(SharedAPIParameters.NODE_NAME_OPTION)){
			optionalParameterIndex = arguments.indexOf(SharedAPIParameters.NODE_NAME_OPTION);
			optionalParameterIndex++;
			nodeName = ConcreteNodeDescriptor.adjustNodeName(arguments.get(optionalParameterIndex));
			optionalParameterIndex++;
		}else{
			try {
				nodeName = InetAddress.getLocalHost().getHostName();
				nodeName = ConcreteNodeDescriptor.adjustNodeName(nodeName);
			} catch (UnknownHostException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * Listens for incoming only one ResponseMessage from the
	 * local rmiregistry.
	 * @return response : ResponseMessage
	 */
	protected ResponseMessage waitingOneIncomingResponse(){
		ResponseMessage response = APIRoutineFunctions.receiveResponse();
		return response;
	}
	
	/**
	 * Waits for a number of 'expectedResponses' of ResponseMessage(s)
	 * @param expectedResponses : int
	 * @return responsesVector : List<ResponseMessage>
	 */
	protected List<ResponseMessage> waitingIncomingResponses(int expectedResponses){
		ResponseMessage reply;
		Vector<ResponseMessage> resposesVector = new Vector<ResponseMessage>(expectedResponses);		
		System.out.println("Waiting for "+expectedResponses+" answers: ");
//		reply = APIRoutineFunctions.receiveResponse();
//		System.out.println("Accepted request. Received reply: "+reply);
		for(int i=0; i < expectedResponses ; i++){
			reply = APIRoutineFunctions.receiveResponse();
			System.out.println("Received reply:\n"+i+") "+reply);
			resposesVector.add(reply);
		}
		return resposesVector;
	}
	
	/**
	 * Create a Requester using the local address from the 
	 * InetAddress class and the name of the local node from 
	 * the NetworkManager.
	 * @return requester : Requester
	 * @throws UnknownHostException
	 */
	protected Requester generateLocalRequester() throws UnknownHostException{
		Requester localRequester;
		InetAddress address = InetAddress.getLocalHost();
		localRequester = new ConcreteRequester(address, registeredServiceName, 
			APIRoutineFunctions.RMIREGISTRY_STADARD_PORT);
		return localRequester;
	}

}
